package com.elong.common.http;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

/**
 * @ClassName: HttpsUtil

 * @Description: https 工具类

 * @author Guanguo.Gao

 * @date 2014年9月17日 下午2:58:20

 * @version V1.0
 */
public class HttpsUtil {

    private static Log log = LogFactory.getLog(HttpsUtil.class);

    private static final String DEFAULT_CHARSET = "UTF-8";

    private static final String METHOD_POST = "POST";
    
    public static String doPost(String url, String params) throws Exception{
        return doPost(url, params, DEFAULT_CHARSET, 25000, 25000);
    }

    public static String doPost(String url, String params, String charset,
            int connectTimeout, int readTimeout) throws Exception {
        String ctype = "application/json;charset=" + charset;
        byte[] content = {};
        if (params != null) {
            content = params.getBytes(charset);
        }

        return doPost(url, ctype, content, connectTimeout, readTimeout);
    }

    public static String doPost(String url, String ctype, byte[] content,
            int connectTimeout, int readTimeout) throws Exception {
        HttpsURLConnection conn = null;
        OutputStream out = null;
        String rsp = null;
        try {
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(new KeyManager[0],
                    new TrustManager[] { new DefaultTrustManager() },
                    new SecureRandom());
            SSLContext.setDefault(ctx);

            conn = getConnection(new URL(url), METHOD_POST, ctype);
            conn.setHostnameVerifier(new HostnameVerifier() {

                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });
            conn.setConnectTimeout(connectTimeout);
            conn.setReadTimeout(readTimeout);
        } catch (Exception e) {
            log.error("request response error, url = " + url);
            throw e;
        }
        try{
            out = conn.getOutputStream();
            out.write(content);
            rsp = getResponseAsString(conn);
        }catch(IOException e){
            log.error("req error, url = " + url, e);
            throw e;
        }
        
        finally {
            if (out != null)
                out.close();
            if (conn != null)
                conn.disconnect();
        }
        return rsp;
    }

    private static class DefaultTrustManager implements X509TrustManager {

        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {

        }

        public X509Certificate[] getAcceptedIssuers() {

            return null;
        }

    }

    private static HttpsURLConnection getConnection(URL url, String method,
            String ctype) throws IOException {
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
        conn.setRequestMethod(method);

        conn.setDoInput(true);
        if(method.equals("POST"))
            conn.setDoOutput(true);
        conn.setRequestProperty("Accept", "text/xml,text/javascript,text/html");
        conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
        conn.setRequestProperty("Content-Type", ctype);
        
        //base 64 encode
        //String loginInfo = "username" + ":" + "passwrod";
        //String encoded = new String(new BASE64Encoder().encode(loginInfo.getBytes()));
        //conn.setRequestProperty("Proxy-Authorization", "Basic " + encoded);
        return conn;
    }

    /**
     * @Description: 从conn中获得返回字符串
     * @param conn
     * @return
     * @throws IOException
     * @throws
     */
    protected static String getResponseAsString(HttpURLConnection conn) throws IOException {
        String charset = getResponseCharset(conn.getContentType());
        InputStream es = conn.getErrorStream();
        if (es == null)
            return getStreamAsString(conn.getInputStream(), charset);
        else {
            String msg = getStreamAsString(es, charset);
            if ("".equals(msg)) {
                throw new IOException(conn.getResponseCode() + ":"
                        + conn.getResponseMessage());
            } else {
                throw new IOException(msg);
            }
        }

    }

    /**
     * @Description: 从字符流中获得字符串
     * @param stream
     * @param charset
     * @return
     * @throws IOException
     * @throws
     */
    public static String getStreamAsString(InputStream stream, String charset) throws IOException {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    stream, charset));
            StringWriter writer = new StringWriter();

            char[] chars = new char[8192];
            int count = 0;
            while ((count = reader.read(chars)) > 0) {
                writer.write(chars, 0, count);
            }

            return writer.toString();
        } finally {
            if (stream != null) {
                stream.close();
            }
        }
    }
    
    /**
     * @Description: 获得返回charset
     * @param ctype
     * @return
     * @throws
     */
    private static String getResponseCharset(String ctype) {  
        String charset = DEFAULT_CHARSET;  
  
        if (!"".equals(ctype)) {  
            String[] params = ctype.split(";");  
            for (String param : params) {  
                param = param.trim();  
                if (param.startsWith("charset")) {  
                    String[] pair = param.split("=", 2);  
                    if (pair.length == 2) {  
                        if (!"".equals(pair[1])) {  
                            charset = pair[1].trim();  
                        }  
                    }  
                    break;  
                }  
            }  
        }  
        return charset;  
    } 
    
    /**
     * @Description: doGet请求
     * @param url
     * @return
     * @throws Exception
     * @throws
     */
    public static String doGet(String url){
        Long startTime = System.currentTimeMillis();
        HttpsURLConnection conn = null;
        String rsp = null;
        try {
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(new KeyManager[0],
                    new TrustManager[] { new DefaultTrustManager() },
                    new SecureRandom());
            SSLContext.setDefault(ctx);
            setProxy();
            conn = getConnection(new URL(url), "GET", "UTF-8");
            conn.setHostnameVerifier(new HostnameVerifier() {

                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });
            conn.setConnectTimeout(25000);
            conn.setReadTimeout(25000);
        } catch (Exception e) {
            log.error("request response error, url = " + url);
        }
        try{
            rsp = getResponseAsString(conn);
        }catch(IOException e){
            log.error("req error, url = " + url, e);
        }finally {
            if (conn != null)
                conn.disconnect();
        }
        System.out.println("cost mils = " + (System.currentTimeMillis() - startTime) + "ms");
        return rsp;
    }
    
    public static void setProxy(){
        //192.168.99.128:3128
        System.setProperty("http.proxyHost", "192.168.99.128");
        System.setProperty("http.proxyPort", "3128");
        System.setProperty("http.proxySet", "true");
    }
    
    /**
     * @Description: 通过httpclient来访问https
     * @return
     * @throws
     */
    public static String doGetByClient(String url){
        Long startTime = System.currentTimeMillis();
        CloseableHttpClient httpClient = createSSLClientDefault();
        HttpGet httpGet = new HttpGet(url);
        //HttpHost proxy = new HttpHost("192.168.99.128", 3128, "http");
        //RequestConfig config = RequestConfig.custom().setProxy(proxy).build();
        //httpGet.setConfig(config);
        HttpResult result = new HttpResult();
        
        try {
            HttpResponse response = httpClient.execute(httpGet);
            result = HttpUtil.getContentFromResponse(response);  
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        System.out.println("cost mils = " + (System.currentTimeMillis() - startTime) + "ms");
        if(result.getRetCode() == 200)
            return result.getRetContent();
        else {
            return "";
        }
    }
    
    /** 创建CloseableHttpClient 
     */
    public static CloseableHttpClient createSSLClientDefault(){
        try {
            SSLContext sslContext = new SSLContextBuilder()
                .loadTrustMaterial(null, new TrustStrategy() {
                    
                    public boolean isTrusted(X509Certificate[] chain, String authType)
                            throws CertificateException {
                        return true;
                    }
                }).build();
            
            //HttpHost proxy = new HttpHost("192.168.99.128", 3128, "http");
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
            CredentialsProvider credProvider = new BasicCredentialsProvider();
            UsernamePasswordCredentials cred = new UsernamePasswordCredentials("test", "test");
            credProvider.setCredentials(
                    new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), cred);
            CloseableHttpClient clients = HttpClients.custom()
                    .setSSLSocketFactory(sslsf)
                    .setHostnameVerifier(new CustomHostNameVerifier())
                    //.setProxy(proxy)
                    .setDefaultCredentialsProvider(credProvider)
                    .build();
            return clients;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return HttpClients.createDefault();
    }
    
    //verifier class
    private static  class CustomHostNameVerifier implements X509HostnameVerifier{

        public boolean verify(String hostname, SSLSession session) {
            return true;
        }

        public void verify(String host, SSLSocket ssl) throws IOException {
        }

        public void verify(String host, X509Certificate cert)
                throws SSLException {
        }

        public void verify(String host, String[] cns, String[] subjectAlts)
                throws SSLException {
        }
        
    }

    
    public static void main(String[] args) throws Exception {
        String content = HttpsUtil.doGetByClient("https://kyfw.12306.cn/otn/login/init");
        System.out.println(content);
    }
    
}
